﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Build.Client;
using System.ComponentModel;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Drawing;
using System.Windows.Threading;

namespace ShahyadS.TFSBuildInspectionModel
{
	public class BuildDefinitionRow : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;
		public event EventHandler RowRefreshed;
		public IQueuedBuild QueuedBuild;
		public BuildDefinitionCollection Parent;
		public Uri BuildDefinitionUri { get; set; }
		public string BuildDefinitionName { get; set; }
		public string Extension { get; set; }
		public string Line { get; set; }
		public string Name { get; set; }
		public string TeamProject { get; set; }
		public bool BuildDefinitionEnabled { get; set; }
		public IBuildController Controller;
		public Object Agent;
		volatile bool _Visible;
		public bool Visible
		{
			get
			{
				return _Visible;
			}
			set
			{
				if (!value)
				{
					LatestBuildRetention = ExtendedBuildRetention.Unknown;
					LatestBuildReason = ExtendedBuildReason.Unknown;
					LatestBuildIsUpdated = UpdateStatus.Unknown;
					LatestGoodBuildIsUpdated = UpdateStatus.Unknown;
					CurrentBuildStatus = ExtendedBuildStatus.Unknown;
					LatestBuildStatus = ExtendedBuildStatus.Unknown;
					Status = BuildDefinitionStatus.Unknown;
					if (Parent!=null && Parent.Contains(this))
					{
						Parent.Remove(this);
					}
				}
				else
				{
					if (Parent != null)
					{
						if (!Parent.Contains(this))
						{
							Parent.Add(this);
						}
						Parent.AsyncRefresh(new List<BuildDefinitionRow>() { this });
					}
				}
				_Visible = value;
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
				}
			}
		}
		volatile bool _IsDirty;
		public bool IsDirty 
		{
			get
			{
				return _IsDirty;
			}
			set
			{
				if (value)
				{
					LatestBuildRetention = ExtendedBuildRetention.Refreshing;
					LatestBuildReason = ExtendedBuildReason.Refreshing;
					LatestBuildIsUpdated = UpdateStatus.Refreshing;
					LatestGoodBuildIsIntegrated = IntegrationStatus.Refreshing;
					LatestGoodBuildIsUpdated = UpdateStatus.Refreshing;
					CurrentBuildStatus = ExtendedBuildStatus.Refreshing;
					LatestBuildStatus = ExtendedBuildStatus.Refreshing;
					Status = BuildDefinitionStatus.Refreshing;
				}
				_IsDirty = value;
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs("IsDirty"));
				}
			}
		}
		internal BuildDefinitionRow(IBuildDefinition buildDefinition, BuildDefinitionCollection parent)
		{
			BuildDefinitionUri = buildDefinition.Uri;
			BuildDefinitionName = buildDefinition.Name;
			Name = buildDefinition.Name.Split('.')[0];
			if (buildDefinition.Name.Split('.').Length > 2)
				Extension = buildDefinition.Name.Split('.')[2];
			else
				Extension = new string(char.MaxValue, 1);
			if (buildDefinition.Name.Split('.').Length > 1)
				Line = buildDefinition.Name.Split('.')[1];
			else
				Line = new string(char.MaxValue, 1);
			IsDirty = true;
			Visible = true;
			TeamProject = buildDefinition.TeamProject;
			BuildDefinitionEnabled = buildDefinition.Enabled;
			Parent = parent;
		}
		volatile Uri _LatestBuildUri;
		public Uri LatestBuildUri
		{
			get
			{
				return _LatestBuildUri;
			}
			set
			{
				_LatestBuildUri = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestBuildUri"));
			}
		}
		volatile Uri _LatestGoodBuildUri;
		public Uri LatestGoodBuildUri
		{
			get
			{
				return _LatestGoodBuildUri;
			}
			set
			{
				_LatestGoodBuildUri = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestGoodBuildUri"));
			}
		}
		volatile BuildDefinitionStatus _Status;
		public BuildDefinitionStatus Status
		{
			get
			{
				return _Status;
			}
			set
			{
				_Status = value;
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs("Status"));
				}
			}
		}
		volatile ExtendedBuildStatus _LatestBuildStatus;
		public ExtendedBuildStatus LatestBuildStatus
		{
			get
			{
				return _LatestBuildStatus;
			}
			set
			{
				_LatestBuildStatus = value;
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs("LatestBuildStatus"));
				}
			}
		}
		volatile ExtendedBuildStatus _CurrentBuildStatus;
		public ExtendedBuildStatus CurrentBuildStatus
		{
			get
			{
				return _CurrentBuildStatus;
			}
			set
			{
				_CurrentBuildStatus = value;
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs("CurrentBuildStatus"));
				}
			}
		}
		volatile string _LatestBuildName;
		public string LatestBuildName
		{
			get
			{
				return _LatestBuildName;
			}
			set
			{
				_LatestBuildName = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestBuildName"));
			}
		}
		public string LatestBuildDropLocation { get; set; }
		volatile string _LatestGoodBuildName;
		public string LatestGoodBuildName
		{
			get
			{
				return _LatestGoodBuildName;
			}
			set
			{
				_LatestGoodBuildName = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestGoodBuildName"));
			}
		}
		volatile ExtendedBuildRetention _LatestBuildRetention;
		public ExtendedBuildRetention LatestBuildRetention
		{
			get
			{
				return _LatestBuildRetention;
			}
			set
			{
				_LatestBuildRetention = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestBuildRetention"));
			}
		}
		volatile ExtendedBuildReason _LatestBuildReason;
		public ExtendedBuildReason LatestBuildReason
		{
			get
			{
				return _LatestBuildReason;
			}
			set
			{
				_LatestBuildReason = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestBuildReason"));
			}
		}
		public DateTime LatestBuildFinishTime { get; set; }
		IntegrationStatus _LatestGoodBuildIsIntegrated;
		public IntegrationStatus LatestGoodBuildIsIntegrated
		{
			get
			{
				return _LatestGoodBuildIsIntegrated;
			}
			private set
			{
				_LatestGoodBuildIsIntegrated = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestGoodBuildIsIntegrated"));
			}
		}
		UpdateStatus _LatestGoodBuildIsUpdated;
		public UpdateStatus LatestGoodBuildIsUpdated
		{
			get
			{
				return _LatestGoodBuildIsUpdated;
			}
			private set
			{
				_LatestGoodBuildIsUpdated = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestGoodBuildIsUpdated"));
			}
		}
		UpdateStatus _LatestBuildIsUpdated;
		public UpdateStatus LatestBuildIsUpdated 
		{
			get
			{
				return _LatestBuildIsUpdated;
			}
			private set
			{
				_LatestBuildIsUpdated = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("LatestBuildIsUpdated"));
			} 
		}
		public bool IsSuccessfull 
		{
			get
			{
				return LatestBuildStatus == ExtendedBuildStatus.Succeeded;
			}
		}
		public string ControllerName
		{
			get
			{
				if (Controller != null)
					return Controller.Name;
				return "";
			}
		}
		public string AgentName 
		{
			get
			{
				if (Agent != null)
				{
					if (Agent is IBuildAgent)
						return (Agent as IBuildAgent).MachineName;
					else
						return (Agent as IBuildController).Name;
				}
				return "";
			}
		}
		public void RefreshQueue()
		{
			if (Parent.AllBuildInQueue != null)
			{
				QueuedBuild = Parent.AllBuildInQueue.Where(q => q.BuildDefinition.Uri == BuildDefinitionUri).FirstOrDefault();
			}
			if (QueuedBuild != null)
			{
				Controller = QueuedBuild.BuildController;
				if (Parent.BuildServer.BuildServerVersion == BuildServerVersion.V3)
				{
					Agent = Parent.Agents.Cast<IBuildAgent>().Where(a => QueuedBuild.Build != null && a.ReservedForBuild == QueuedBuild.Build.Uri).FirstOrDefault();
				}
				else
				{
					Agent = Controller;
				}
				if (Agent != null)
					CurrentBuildStatus = ExtendedBuildStatus.InProgress;
				else if (Controller != null)
					CurrentBuildStatus = ExtendedBuildStatus.NotStarted;
				else
					CurrentBuildStatus = ExtendedBuildStatus.None;
			}
			else
			{
				Controller = null;
				Agent = null;
				CurrentBuildStatus = ExtendedBuildStatus.None;
			}
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs("ControllerName"));
				PropertyChanged(this, new PropertyChangedEventArgs("AgentName"));
			}
		}
		public void Exclude()
		{
			Visible = false;
		}
		public void Include()
		{
			Visible = true;
		}
		public void Refresh()
		{
			if (!Visible || !IsDirty)
				return;
			lock (this)
			{
				IsDirty = true;
				RefreshQueue();
				var buildDefinition = Parent.BuildServer.QueryBuildDefinitionsByUri(new Uri[] { BuildDefinitionUri }).FirstOrDefault();
				Name = buildDefinition.Name.Split('.')[0];
				if (buildDefinition.Name.Split('.').Length > 2)
					Extension = buildDefinition.Name.Split('.')[2];
				else
					Extension = new string(char.MaxValue, 1);
				if (buildDefinition.Name.Split('.').Length > 1)
					Line = buildDefinition.Name.Split('.')[1];
				else
					Line = new string(char.MaxValue, 1);
				TeamProject = buildDefinition.TeamProject;
				BuildDefinitionEnabled = buildDefinition.Enabled;

				IBuildDetail LatestBuild = buildDefinition.GetLatestBuild();
				if (LatestBuild != null)
				{
					LatestBuildUri = LatestBuild.Uri;
					LatestBuildName = LatestBuild.BuildNumber;
					LatestBuildRetention = LatestBuild.KeepForever? ExtendedBuildRetention.IsRetained : ExtendedBuildRetention.IsNotRetained;
					switch (LatestBuild.Reason)
					{
						case BuildReason.BatchedCI:
							LatestBuildReason = ExtendedBuildReason.BatchedCI;
							break;
						case BuildReason.Schedule:
							LatestBuildReason = ExtendedBuildReason.Schedule;
							break;
						default:
							LatestBuildReason = ExtendedBuildReason.None;
							break;
					}
					switch (LatestBuild.Status)
					{
						case BuildStatus.Failed:
							LatestBuildStatus = ExtendedBuildStatus.Failed;
							break;
						case BuildStatus.InProgress:
							LatestBuildStatus = ExtendedBuildStatus.InProgress;
							break;
						case BuildStatus.None:
							LatestBuildStatus = ExtendedBuildStatus.None;
							break;
						case BuildStatus.NotStarted:
							LatestBuildStatus = ExtendedBuildStatus.NotStarted;
							break;
						case BuildStatus.PartiallySucceeded:
							LatestBuildStatus = ExtendedBuildStatus.PartiallySucceeded;
							break;
						case BuildStatus.Stopped:
							LatestBuildStatus = ExtendedBuildStatus.Stopped;
							break;
						case BuildStatus.Succeeded:
							LatestBuildStatus = ExtendedBuildStatus.Succeeded;
							break;
						default:
							LatestBuildStatus = ExtendedBuildStatus.None;
							break;
					}
					LatestBuildFinishTime = LatestBuild.FinishTime;
					LatestBuildDropLocation = LatestBuild.DropLocation;
				}
				else
				{
					LatestBuildUri = null;
					LatestBuildName = "";
					LatestBuildRetention = ExtendedBuildRetention.IsNotRetained;
					LatestBuildReason = ExtendedBuildReason.None;
					LatestBuildStatus = ExtendedBuildStatus.None;
					LatestBuildFinishTime = DateTime.MaxValue;
					LatestBuildIsUpdated = UpdateStatus.Outdated;
				}

				IBuildDetail LatestGoodBuild = LatestBuild;
				if (LatestBuild==null || LatestBuild.Status!=BuildStatus.Succeeded)
				{
					LatestGoodBuild = buildDefinition.GetLatestGoodBuild();
				}

				if (LatestBuild != null)
				{
					try
					{
						LatestBuildIsUpdated = UpdateStatus.Updated;
						foreach (var m in buildDefinition.Workspace.Mappings)
						{
							VersionSpec sFrom = VersionSpec.ParseSingleSpec("L" + LatestBuild.LabelName, null);
							VersionSpec sTo = VersionSpec.Latest;
							var changesets = Parent.VersionControlServer.QueryHistory(
								m.ServerItem,
								sTo,
								0,
								RecursionType.Full,
								null,
								sFrom,
								sTo,
								int.MaxValue,
								false,
								false).OfType<Changeset>(); // Slotmode to false
							if (changesets.Any())
							{
								LatestBuildIsUpdated = UpdateStatus.Outdated;
								break;
							}
						}
					}
					catch (Exception ex)
					{
						LatestBuildIsUpdated = UpdateStatus.Outdated;
					}
				}
				if (LatestGoodBuild != null)
				{
					LatestGoodBuildName = LatestGoodBuild.BuildNumber;
					LatestGoodBuildUri = LatestGoodBuild.Uri;
					if (LatestGoodBuild == LatestBuild)
					{
						LatestGoodBuildIsUpdated = LatestBuildIsUpdated;
					}
					else
					{
						try
						{
							LatestGoodBuildIsUpdated = UpdateStatus.Updated;
							foreach (var m in buildDefinition.Workspace.Mappings)
							{
								VersionSpec sFrom = VersionSpec.ParseSingleSpec("L" + LatestGoodBuild.LabelName, null);
								VersionSpec sTo = VersionSpec.Latest;
								var changesets = Parent.VersionControlServer.QueryHistory(
									m.ServerItem,
									sTo,
									0,
									RecursionType.Full,
									null,
									sFrom,
									sTo,
									int.MaxValue,
									false,
									false).OfType<Changeset>(); // Slotmode to false
								if (changesets.Any())
								{
									LatestGoodBuildIsUpdated = UpdateStatus.Outdated;
									break;
								}
							}
						}
						catch (Exception ex)
						{
							LatestGoodBuildIsUpdated = UpdateStatus.Outdated;
						}
					}
					LatestGoodBuildIsIntegrated = IntegrationStatus.Integrated;
					string[] LatestGoodBuildAncestors = LatestGoodBuild.GetBuildCustomInfo().BuildAncestorInfo.Split(';').Select(a => a.Trim()).Where(a => !String.IsNullOrEmpty(a)).ToArray();
					string[] LatestGoodBuildAncestorDefs = LatestGoodBuildAncestors.Select(a=>new string(a.Take(a.LastIndexOf('_')).ToArray())).ToArray();
					var ancestorRows = Parent.Where(r => LatestGoodBuildAncestorDefs.Contains(r.BuildDefinitionName)).ToList();
					if (LatestGoodBuildAncestors.Length == 0)
					{
						LatestGoodBuildIsIntegrated = IntegrationStatus.Integrated;
					}
					else if (LatestGoodBuildAncestors.Length != ancestorRows.Count)
					{
						LatestGoodBuildIsIntegrated = IntegrationStatus.NotIntegrated;
					}
					else
					{
						ancestorRows.ToList().ForEach(r => r.Include());
						ancestorRows.ToList().ForEach(r => r.Refresh());
						LatestGoodBuildIsIntegrated = ancestorRows.All(r => r.LatestGoodBuildIsUpdated == UpdateStatus.Updated && LatestGoodBuildAncestors.Contains(r.LatestGoodBuildName))? IntegrationStatus.Integrated : IntegrationStatus.NotIntegrated;
					}
				}
				else
				{
					LatestGoodBuildUri = null;
					LatestGoodBuildName = "";
					LatestGoodBuildIsUpdated = UpdateStatus.Outdated;
					LatestGoodBuildIsIntegrated = IntegrationStatus.NotIntegrated;
				}

				if (IsSuccessfull || LatestGoodBuildIsUpdated == UpdateStatus.Updated)
				{
					if (IsSuccessfull && LatestBuildIsUpdated== UpdateStatus.Updated && LatestGoodBuildIsIntegrated == IntegrationStatus.Integrated)
						Status = BuildDefinitionStatus.Ok;
					else if (LatestBuildIsUpdated != UpdateStatus.Updated || LatestGoodBuildIsIntegrated != IntegrationStatus.Integrated)
						Status = BuildDefinitionStatus.OkButQueue;
					else
						Status = BuildDefinitionStatus.OkIfDelete;
				}
				else
				{
					if (LatestBuildIsUpdated == UpdateStatus.Updated)
						Status = BuildDefinitionStatus.Failed;
					else
						Status = BuildDefinitionStatus.Failed;
				}
				IsDirty = false;
			}
			if (RowRefreshed != null)
			{
				OnRefreshed(null);
			}
		}
		protected void OnRefreshed(EventArgs e)
		{
			EventHandler rowRefreshed = this.RowRefreshed;
			if (rowRefreshed != null)
			foreach (EventHandler nh in RowRefreshed.GetInvocationList())
			{
				DispatcherObject dispObj = nh.Target as DispatcherObject;
				ISynchronizeInvoke syncInvoke = nh.Target as ISynchronizeInvoke;
				if (dispObj != null)
				{
					Dispatcher dispatcher = dispObj.Dispatcher;
					if (dispatcher != null && !dispatcher.CheckAccess())
					{
						dispatcher.BeginInvoke((Action)(() => nh.Invoke(this, e)));
						continue;
					}
				}
				else if (syncInvoke != null && syncInvoke.InvokeRequired)
				{
					syncInvoke.BeginInvoke((Action)(() => {nh.Invoke(this, e);}), null);
					continue;
				}
				nh.Invoke(this, e);
			}
		}
	}
}
